package com.qsw.crashcollectionlib.core;

import android.os.Process;
import android.text.TextUtils;
import android.util.Log;

import com.qsw.crashcollectionlib.exception.PassException;
import com.qsw.crashcollectionlib.utils.ExceptionLogUtils;

import java.io.File;
import java.io.FilenameFilter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Calendar;
import java.util.regex.Pattern;

/**
 * 未捕获异常处理类
 */
public class CrashExceptionProcesser implements UncaughtExceptionHandler {

    /**
     * 默认的异常日志文件路径
     */
    private static String defaultLogPath;
    /**
     * 默认的异常日志文件名前缀
     */
    private static String defaultLogNamePrefix;
    /**
     * 默认的异常日志文件名后缀，不能含有“_”
     */
    private static String defaultLogNameSuffix;
    /**
     * 默认的异常日志文件名
     */
    private static String defaultLogName;
    /**
     * 默认的异常日志文件名是否根据日期变化
     */
    private static boolean defaultLogNameByDate = true;
    /**
     * 默认日志文件最大长度，超过该长度则创建新文件
     */
    private static int defaultLogFileMaxLength = 2 * 1024 * 1024;// 单位b
    /**
     * 默认日志文件命名检测间隔
     */
    private static long defaultLogFileNameCheckInterval = 3 * 1000;// 单位ms
    /**
     * 默认的异常日志文件名索引
     */
    private static int defaultLogNameIndex;
    private static boolean defaultLogNameIndexInit = false;
    private static int year;
    private static int dayOfYear;
    /**
     * 日志文件命名上次检测时间
     */
    private static long lastCheckNameTime = 0;

    private static CrashExceptionProcesser mSingleCrashExceptionProcesser;

    private CrashExceptionProcesser() {
        init();
    }

    /**
     * 获取单例模式的对象，通过该方法获取的对象为同一个对象
     *
     * @return CrashExceptionHandler对象
     */
    public static CrashExceptionProcesser getInstance() {
        if (mSingleCrashExceptionProcesser == null) {
            synchronized (CrashExceptionProcesser.class) {
                if (mSingleCrashExceptionProcesser == null) {
                    mSingleCrashExceptionProcesser = new CrashExceptionProcesser();
                }
            }
        }
        return mSingleCrashExceptionProcesser;
    }

    /**
     * 初始化异常处理类
     */
    private void init() {
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 初始化日志文件
     *
     * @param defaultCrashLogPath        默认的异常日志存储路径
     * @param defaultCrashLogNamePrefix  默认的异常日志文件名前缀
     * @param defaultCrashLogNameSufffix 默认的异常日志文件名后缀，不能包含“_”
     */
    public void initLogFileSetting(String defaultCrashLogPath, String defaultCrashLogNamePrefix, String defaultCrashLogNameSufffix) {
        setDefaultLogPath(defaultCrashLogPath);
        setDefaultLogNamePrefixSuffix(defaultCrashLogNamePrefix, defaultCrashLogNameSufffix);
    }

    @Override
    public void uncaughtException(final Thread thread, final Throwable ex) {
        if (ex instanceof PassException) {
            ex.printStackTrace();
            return;
        }
        /*if (ex.getMessage().contains("Cannot perform this operation because the connection pool has been closed")) {
            ex.printStackTrace();
            return;
        }*/
        handleException(ex);
//        stopApplication(1500);
    }

    /**
     * 处理异常
     *
     * @param ex 要处理的异常
     */
    private void handleException(Throwable ex) {
        ExceptionLogUtils.saveException2Log(ex);
    }

    /**
     * 延迟指定时间后停止调用者进程
     *
     * @param delayTimeMillisecond 延迟时间
     */
    private void stopApplication(int delayTimeMillisecond) {
        try {
            Thread.sleep(delayTimeMillisecond);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            Process.killProcess(Process.myPid());
            System.exit(1);
        }
    }

    public static String getDefaultLogPath() {
        return defaultLogPath;
    }

    private void setDefaultLogPath(String defaultLogPath) {
        CrashExceptionProcesser.defaultLogPath = defaultLogPath;
        defaultLogNameIndexInit = false;
        defaultLogNameIndex = 0;
    }

    public static String getDefaultLogName() {
        checkDefaultLogFileName();
        return defaultLogName;
    }

    /**
     * 设置默认日志文件前缀、后缀，后缀不能含有“_”
     *
     * @param defaultLogNamePrefix 默认日志文件前缀
     * @param defaultLogNameSuffix 默认日志文件后缀，不能含有“_”
     */
    private void setDefaultLogNamePrefixSuffix(String defaultLogNamePrefix, String defaultLogNameSuffix) {
        CrashExceptionProcesser.defaultLogNamePrefix = defaultLogNamePrefix;
        if (defaultLogNameSuffix != null && !defaultLogNameSuffix.contains("_")) {
            CrashExceptionProcesser.defaultLogNameSuffix = defaultLogNameSuffix;
        }
        defaultLogNameIndexInit = false;
        defaultLogNameIndex = 0;
        year = 0;
        dayOfYear = 0;
        lastCheckNameTime = 0;
        checkDefaultLogFileName();
    }

    public static boolean isDefaultLogNameByDate() {
        return defaultLogNameByDate;
    }

    public static void setDefaultLogNameByDate(boolean defaultLogNameByDate) {
        CrashExceptionProcesser.defaultLogNameByDate = defaultLogNameByDate;
    }

    public static int getDefaultLogFileMaxLength() {
        return defaultLogFileMaxLength;
    }

    public static void setDefaultLogFileMaxLength(int defaultLogFileMaxLength) {
        CrashExceptionProcesser.defaultLogFileMaxLength = defaultLogFileMaxLength;
    }

    public static long getDefaultLogFileNameCheckInterval() {
        return defaultLogFileNameCheckInterval;
    }

    public static void setDefaultLogFileNameCheckInterval(long defaultLogFileNameCheckInterval) {
        CrashExceptionProcesser.defaultLogFileNameCheckInterval = defaultLogFileNameCheckInterval;
    }

    /**
     * 检查文件名是否需要改变
     */
    private static void checkDefaultLogFileName() {
        if (System.currentTimeMillis() < lastCheckNameTime) {
            lastCheckNameTime = 0;
        }
        if (System.currentTimeMillis() - lastCheckNameTime > defaultLogFileNameCheckInterval) {
            lastCheckNameTime = System.currentTimeMillis();
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(lastCheckNameTime);
            checkDefaultLogByDate(calendar);
            checkDefaultLogFileSize(calendar);
        }
    }

    /**
     * 检查是否需要根据日期改变默认文件名
     *
     * @param calendar 日期时间
     */
    private static void checkDefaultLogByDate(Calendar calendar) {
        if (defaultLogNameByDate) {
            if (calendar.get(Calendar.YEAR) != year || calendar.get(Calendar.DAY_OF_YEAR) != dayOfYear) {
                year = calendar.get(Calendar.YEAR);
                dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
                /*int month = calendar.get(Calendar.MONTH) + 1;
                int day = calendar.get(Calendar.DAY_OF_MONTH);
                int lastIndex = defaultLogName.lastIndexOf("_");
                if (lastIndex > 0) {
                    String lastStr = defaultLogName.substring(lastIndex);
                    if (lastStr.contains("D")) {
                        defaultLogName = defaultLogName.substring(0, lastIndex);
                    } else {
                        String tmpStr = defaultLogName.substring(0, lastIndex);
                        lastIndex = tmpStr.lastIndexOf("_");
                        if (lastIndex > 0) {
                            defaultLogName = defaultLogName.substring(0, lastIndex);
                        }
                    }
                }*/
                defaultLogName = createLogName(calendar, 0);
                defaultLogNameIndex = 0;
            }
        }
    }

    /**
     * 检查文件是否过大，若过大则在文件名后添加索引创建新文件
     *
     * @param calendar 日期时间
     */
    private static void checkDefaultLogFileSize(Calendar calendar) {
        if (!defaultLogNameIndexInit) {
            File defaultLogPathFile = new File(defaultLogPath);
            defaultLogPathFile.listFiles(new FilenameFilter() {
                boolean result;
                final String defNameStr = (
                        (!TextUtils.isEmpty(defaultLogNameSuffix) && defaultLogName.contains(defaultLogNameSuffix))
                                ? defaultLogName.substring(0, defaultLogName.lastIndexOf(defaultLogNameSuffix))
                                : defaultLogName
                ) + "_";
                final Pattern pattern = Pattern.compile("[0-9]*");
                int suffixLength = TextUtils.isEmpty(defaultLogNameSuffix) ? 0 : defaultLogNameSuffix.length();

                @Override
                public boolean accept(File file, String s) {
                    result = s.startsWith(defNameStr);
                    if (result) {
                        String indexStr = s.substring(s.indexOf(defNameStr) + defNameStr.length(), s.length() - suffixLength);
                        result = pattern.matcher(indexStr).matches();
                        if (result) {
                            int i = Integer.parseInt(indexStr);
                            defaultLogNameIndex = Math.max(defaultLogNameIndex, i);
                        }
                    }
                    return result;
                }
            });
            if (defaultLogNameIndex > 0) {
                defaultLogName = createLogName(calendar, defaultLogNameIndex);
            }
            defaultLogNameIndexInit = true;
        }
        File file = new File(defaultLogPath, defaultLogName);
        if (file.exists() && file.length() > defaultLogFileMaxLength) {
            /*if (defaultLogNameIndex > 0) {
                int lastIndex = defaultLogName.lastIndexOf("_");
                if (lastIndex > 0) {
                    defaultLogName = defaultLogName.substring(0, lastIndex);
                }
            }*/
            defaultLogNameIndex++;
            defaultLogName = createLogName(calendar, defaultLogNameIndex);
        }
    }

    /**
     * 生成一个日志文件名
     *
     * @param calendar 生成所需的日期
     * @param index    生成所需的后缀索引
     * @return 日志文件名
     */
    private static String createLogName(Calendar calendar, int index) {
        StringBuilder sb = new StringBuilder(defaultLogNamePrefix);
        if (calendar != null) {
            int month = calendar.get(Calendar.MONTH) + 1;
            int day = calendar.get(Calendar.DAY_OF_MONTH);
            int hour = calendar.get(Calendar.HOUR_OF_DAY);
            int minute = calendar.get(Calendar.MINUTE);
            sb.append("_D"
                    + year
                    + (month < 10 ? "0" + month : month)
                    + (day < 10 ? "0" + day : day)
                    + (hour < 10 ? "0" + hour : hour)
                    + (minute < 10 ? "0" + minute : minute)
            );
        }
        if (index > 0) {
            sb.append("_" + index);
        }
        if (!TextUtils.isEmpty(defaultLogNameSuffix)) {
            sb.append(defaultLogNameSuffix);
        }
        return sb.toString();
    }

}
